<!doctype html>
<html lang="en">
<head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">

        <title> Tutorial 31 - PN Triangles Tessellation </title>

        <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:400,600">
        <link rel="stylesheet" href="../style.css">
        <link rel="stylesheet" href="../print.css" media="print">
</head>
<body>
        <header id="header">
                <div>
                        <h2> Tutorial 31: </h2>
                        <h1> PN Triangles Tessellation </h1>
                </div>

                <a id="logo" class="small" href="../../index.html" title="Homepage">
                        <img src="..//logo ldpi.png">
                </a>
        </header>

        <article id="content" class="breakpoint">
                <section>
                        <h3> Background </h3>

                        <p>
                                In the <a href="http://ogldev.atspace.co.uk/www/tutorial30/tutorial30.html">previous tutorial</a> we
                                got introduced to Tessellation in OpenGL 4.x (this tutorial relies heavily on the material covered by
                                the previous one so make sure you are familiar with it). We enabled all the relevant stages and learned how to subdivide
                                our mesh and displace the vertices that were created by the Tessellation process in order to transform
                                a dull flat quad into a complex rocky terrain. Usage of the Tessellation pipeline was fairly simple, though.
                                The evaluation in the TES was just an interpolation of the vertices of the original triangle
                                using the barycentric coordinates generated by the PG. Since the results of the interpolation were
                                located on the plane of the original triangle we had to use displacement mapping in order to create
                                bumps on the surface.
                        </p>
                        <p>
                                In this tutorial we will explore a more advanced Tessellation technique known as <i>PN (Point-Normal) Triangles</i>.
                                This technique was the subject of a <a href="http://alex.vlachos.com/graphics/CurvedPNTriangles.pdf">2001
                                paper by Vlachos et al</a> and was also covered in a GDC2011 presenation called
                                <a href="http://www.nvidia.com/content/PDF/GDC2011/John_McDonald.pdf">"Tessellation On Any Budget"</a> by John McDonald.
                                The idea explored by these papers was to replace each triangle in the original mesh by a geometric
                                surface known as a <i><a href="http://en.wikipedia.org/wiki/B%C3%A9zier_surface">Bezier Surface</a></i> in order
                                to smooth out a low polygon mesh.
                        </p>
                        <p>
                                Bezier Surfaces were invented by Pierre Bezier in the 1960s as a method of describing the curves of
                                automobile bodies. In a nutshell, a Bezier Surface is polynomial function which described a smooth
                                and continuous surface which is fully contained within a set of control points (CP). The polynomial has
                                a special attribute whereas by moving a CP the surface is affected mostly in the vicinity
                                of that CPs. The effect becomes less visible as we move away from that CP. You
                                can picture this as a highly delicate and flexible cloth lying on the floor. If you pull the cloth upwards at a specific
                                point the curve that will be formed will become less and less noticeable in the distant parts of the cloth
                                (if the cloth was infinitely flexible the effect may even become non-existant at some point).
                        </p>
                        <p>
                                The polynomial
                                of the Bezier surface is defined over the unit square. That is, by plugging into the function various
                                combinations of two numbers in the range [0-1] we get a point in 3D space which is exactly on the smooth
                                surface that the polynomial describes. If you plug in many pairs of numbers in the unit square and plot
                                the result on the screen you will eventually get a good approximation of the surface.
                        </p>
                        <p>
                                We are going to use a special case of a Bezier Surface called a <i>Bezier Triangle</i> which
                                has the following form:</p>
                        <img class="center" src="bezier_tri.jpg">
                        <p>
                                Let's decipher this step by step. 'u/v/w' are barycentric coordinates (i.e. they always maintain
                                the equation 'u + v + w = 1'). The ten 'B<sub>xyz</sub>' are CPs. We are going to deviate
                                a bit from the classical definition of a Bezier Triangle and place the CPs as follows:
                        </p>
                        <img class="center" src="control_points.jpg">
                        <p>
                                As you can see, the general form of the CPs resembles a somewhat puffy surface on top of a triangle.
                                By evaluating a lot of barycentric coordinates in the polynomial above we will get an exproximation of
                                that surface in 3D space.
                        </p>
                        <p>
                                Let's see how to integrate these mathematical concepts into the Tessellation pipeline. We are going
                                to start with a triangle and this will be our input patch (same as in the previous tutorial). We will
                                generate the 10 CPs and determine the TLs in the TCS. The PG will subdivide the triangle domain according to the TLs
                                and the TES will be executed for each new point. The TES will plug the barycentric coordinates from the PG
                                and the 10 CPs from the TCS into the polynomial of the Bezier triangle and the result will be a coordinate
                                on the puffy surface. From here on things will run as usual.
                        </p>
                        <p>
                                The one thing we still need to figure out is how to generate the CPs. The method suggested by the PN Triangles
                                algorithm is as follows:
                                <ul>
                                        <li>The original vertices of the triangle remain unchanged (and are named B<sub>003</sub>,
                                                B<sub>030</sub> and B<sub>300</sub>).</li>
                                        <li>Two midpoints are generated on each edge - one on 1/3 of the way the other on 2/3.</li>
                                        <li>Each midpoint is projected on the plane created by the nearest vertex and its normal:</li>
                                </ul>
                        </p>
                        <img class="center" src="cp_projection.jpg">
                        <p>
                                The picture above shows the triangle from the side. Each of the two endpoints has its own normal (in
                                green) from the original mesh. The combination of a point and a normal creates a plane. We take
                                the two midpoints that were calculated earlier and project them to the plane of the nearest vertex (see
                                the dashed arrows).
                        </p>
                        <p>
                                <ul>
                                        <li>In order to calculate the position of B<sub>111</sub> we take a vector from the original
                                        triangle center (average of the three vertices) to the average of the 6 midpoints (after
                                projection). We continue along that vector for one half of its length.</li>
                                </ul>
                        </p>
                        <p>
                                The reasoning behind this scheme is very simple. When you have an area of the mesh which is fairly
                                flat it means that most vertex normals there will point towards the same general direction which will
                                not be far off from the true triangle normal. This means that when we project the midpoints on the
                                planes they would not move away very far from the triangle surface. This will result in a mild "puffiness"
                                in that area. But if the area is very curved it means the midpoints would move further away to overcome
                                the jagged nature of that area. In the demo you can see that we start with a low polygon model of
                                Suzanne, Blender's mascot character, which has about 500 polygons. Breaking of the silhouette is very
                                noticeable, particularly around Suzanne's head. By projecting the midpoints as described above to
                                create CPs and using the Tessellator to evaluate the Bezier Triangle created by this CPs we are able
                                to provide a much smoother model without any artistic resources.
                        </p>
                        <p>
                        <b>References:</b>
                                <ul>
                                        <li>Vlachos Alex, Jorg Peters, Chas Boyd and Jason L. Mitchell. "Curved PN Triangles". Proceedings
                                        of the 2001 Symposium interactive 3D graphics (2001): 159-66.</li>
                                        <li>John McDonald. "Tessellation On Any Budget". Game Developers Conference, 2011.</li>
                                </ul>
                        </p>
                </section>

                <section>
                        <h3> Source walkthru </h3>

                        <p>(lighting.vs:13)</p>
                        <code>
                        void main()<br>
                        {                                                                                        <br>
                         &nbsp; &nbsp;     WorldPos_CS_in = (gWorld * vec4(Position_VS_in, 1.0)).xyz;                           <br>
                         &nbsp; &nbsp;     TexCoord_CS_in = TexCoord_VS_in;                                                     <br>
                         &nbsp; &nbsp;     Normal_CS_in   = <b>normalize((gWorld * vec4(Normal_VS_in, 0.0)).xyz);</b>                <br>
                        }
                        </code>
                        <p>
                        The VS contains only one change from the previous tutorial - the normal must be normalized after
                        the world transformation. The reason is that the TCS relies on the normal having a unit length. Otherwise,
                        the new CPs above the surface won't be generated correctly. If the world transformation contains a scaling operation
                        the normals won't have unit length and have to be normalized.
                        </p>
                        <p>(lighting.cs)</p>
                        <code>
                        #version 410 core<br>
                        <br>
                        // define the number of CPs in the output patch<br>
                        <b>layout (vertices = 1) out;</b><br>
                        <br>
                        <b>uniform float gTessellationLevel;</b><br>
                        <br>
                        // attributes of the input CPs<br>
                        in vec3 WorldPos_CS_in[];<br>
                        in vec2 TexCoord_CS_in[];<br>
                        in vec3 Normal_CS_in[];<br>
                        <br>
                        <b>struct OutputPatch<br>
                        {<br>
                        &nbsp; &nbsp;    vec3 WorldPos_B030;<br>
                        &nbsp; &nbsp;    vec3 WorldPos_B021;<br>
                        &nbsp; &nbsp;    vec3 WorldPos_B012;<br>
                        &nbsp; &nbsp;    vec3 WorldPos_B003;<br>
                        &nbsp; &nbsp;    vec3 WorldPos_B102;<br>
                        &nbsp; &nbsp;    vec3 WorldPos_B201;<br>
                        &nbsp; &nbsp;    vec3 WorldPos_B300;<br>
                        &nbsp; &nbsp;    vec3 WorldPos_B210;<br>
                        &nbsp; &nbsp;    vec3 WorldPos_B120;<br>
                        &nbsp; &nbsp;    vec3 WorldPos_B111;<br>
                        &nbsp; &nbsp;    vec3 Normal[3];<br>
                        &nbsp; &nbsp;    vec2 TexCoord[3];<br>
                        };<br>
                        <br>
                        // attributes of the output CPs<br>
                        out patch OutputPatch oPatch;<br>
                        </b>
                        </code>
                        <p>
                                This is the start of the TCS with the changes marked in bold face. The first thing to note is
                                that we are outputing a single CP. You may find this odd since the whole idea behind PN Triangles is to
                                create a Bezier triangle with 10 CPs on top of the original triangle. So why are we declaring a single
                                output CP instead of 10? the reason is that the main TCS function will be executed as many times as
                                the defined output CPs value. In this algorithm we need to treat some of the points a bit differently
                                than the others which makes it a bit difficult to use the same function for all points. Instead, I've
                                encapsulated all the data of the output patch in the OutputPatch struct above and declared an output
                                variable called oPatch of that type. The TCS main function will run once for each patch and this struct
                                will be populated with data for all the 10 CPs. The implementation that McDonald presented in GDC 2011 (see
                                references) provides a version which may be more efficient. In his version the TCS is executed three times
                                which enables the GPU to distribute the work of a single patch across three threads. In general, if the output
                                CPs are generated using the same algorithm it is better (from a performance point of view) to implement
                                that algorithm as-is in the TCS and have it execute for as many output CPs as you need.
                        </p>
                        <p>
                                Another thing to note is that oPatch is prefixed by the builtin keyword <i>patch</i>. This keyword says
                                that the variable contains data which pertains to the entire patch and not the current output CP. The compiler
                                can use that as a hint to make sure that the code that updates such a variable will run once per patch
                                instead of once per CP (since GPUs will strive to update each output CP in a different HW thread).
                        </p>
                        <p>
                                The final change in this section is that the eye position uniform variable has been replaced with a
                                tessellation level variable. Instead of setting the TL according to the distance from this camera
                                (as in the previous tutorial) we allow the user to configure it using the '+' and '-' keys. This
                                makes it simpler to stand close to the model and see the effect of changing the TL.
                        </p>
                        <p>(lighting.cs:76)</p>
                        <code>
                        void main()<br>
                        {<br>
                        &nbsp; &nbsp;    // Set the control points of the output patch<br>
                        &nbsp; &nbsp;    for (int i = 0 ; i &lt; 3 ; i++) {<br>
                        &nbsp; &nbsp;&nbsp; &nbsp;        oPatch.Normal[i]   = Normal_CS_in[i];<br>
                        &nbsp; &nbsp;&nbsp; &nbsp;        oPatch.TexCoord[i] = TexCoord_CS_in[i];<br>
                        &nbsp; &nbsp;    }<br>
                        <br>
                        &nbsp; &nbsp;    CalcPositions();<br>
                        <br>
                        &nbsp; &nbsp;    // Calculate the tessellation levels<br>
                        &nbsp; &nbsp;    gl_TessLevelOuter[0] = gTessellationLevel;<br>
                        &nbsp; &nbsp;    gl_TessLevelOuter[1] = gTessellationLevel;<br>
                        &nbsp; &nbsp;    gl_TessLevelOuter[2] = gTessellationLevel;<br>
                        &nbsp; &nbsp;    gl_TessLevelInner[0] = gTessellationLevel;<br>
                        }
                        </code>
                        <p>
                                This is the main function of the TCS. The three normals and texture coordinates are copied as-is
                                from the input into the output patch. The 10 CPs that we are going to generate contain only a position
                                value. This is done in a dedicated function called CalcPositions() which is executed next. Finally,
                                the TLs are set according to the uniform variable.
                        </p>
                        <p>(lighting.cs:41)</p>
                        <code>
                        void CalcPositions(<br>
                        {<br>
                        &nbsp; &nbsp;    // The original vertices stay the same<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B030 = WorldPos_CS_in[0];<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B003 = WorldPos_CS_in[1];<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B300 = WorldPos_CS_in[2];<br>
                        <br>
                        &nbsp; &nbsp;    // Edges are names according to the opposing vertex<br>
                        &nbsp; &nbsp;    vec3 EdgeB300 = oPatch.WorldPos_B003 - oPatch.WorldPos_B030;<br>
                        &nbsp; &nbsp;    vec3 EdgeB030 = oPatch.WorldPos_B300 - oPatch.WorldPos_B003;<br>
                        &nbsp; &nbsp;    vec3 EdgeB003 = oPatch.WorldPos_B030 - oPatch.WorldPos_B300;<br>
                        <br>
                        &nbsp; &nbsp;    // Generate two midpoints on each edge<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B021 = oPatch.WorldPos_B030 + EdgeB300 / 3.0;<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B012 = oPatch.WorldPos_B030 + EdgeB300 * 2.0 / 3.0;<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B102 = oPatch.WorldPos_B003 + EdgeB030 / 3.0;<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B201 = oPatch.WorldPos_B003 + EdgeB030 * 2.0 / 3.0;<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B210 = oPatch.WorldPos_B300 + EdgeB003 / 3.0;<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B120 = oPatch.WorldPos_B300 + EdgeB003 * 2.0 / 3.0;<br>
                        <br>
                        &nbsp; &nbsp;    // Project each midpoint on the plane defined by the nearest vertex and its normal<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B021 = ProjectToPlane(oPatch.WorldPos_B021, oPatch.WorldPos_B030,<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  oPatch.Normal[0]);<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B012 = ProjectToPlane(oPatch.WorldPos_B012, oPatch.WorldPos_B003,<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;oPatch.Normal[1]);<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B102 = ProjectToPlane(oPatch.WorldPos_B102, oPatch.WorldPos_B003,<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;oPatch.Normal[1]);<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B201 = ProjectToPlane(oPatch.WorldPos_B201, oPatch.WorldPos_B300,<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;oPatch.Normal[2]);<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B210 = ProjectToPlane(oPatch.WorldPos_B210, oPatch.WorldPos_B300,<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;oPatch.Normal[2]);<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B120 = ProjectToPlane(oPatch.WorldPos_B120, oPatch.WorldPos_B030,<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;oPatch.Normal[0]);<br>
                        <br>
                        &nbsp; &nbsp;    // Handle the center<br>
                        &nbsp; &nbsp;    vec3 Center = (oPatch.WorldPos_B003 + oPatch.WorldPos_B030 + oPatch.WorldPos_B300) / 3.0;<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B111 = (oPatch.WorldPos_B021 + oPatch.WorldPos_B012 + oPatch.WorldPos_B102 +<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; oPatch.WorldPos_B201 + oPatch.WorldPos_B210 + oPatch.WorldPos_B120) / 6.0;<br>
                        &nbsp; &nbsp;    oPatch.WorldPos_B111 += (oPatch.WorldPos_B111 - Center) / 2.0;<br>
                        }
                        </code>
                        <p>
                                This function builds the Bezier triangle on top of the original triangle according to the method
                                described in the background section. The names of the relevant members of the OutputPatch structure
                                match the picture above to make it easier to review. The logic is very simple and follows the algorithm
                                pretty much step by step.
                        </p>
                        <p>(lighting.cs:32)</p>
                        <code>
                        vec3 ProjectToPlane(vec3 Point, vec3 PlanePoint, vec3 PlaneNormal)<br>
                        {<br>
                        &nbsp; &nbsp;     vec3 v = Point - PlanePoint;<br>
                        &nbsp; &nbsp;     float Len = dot(v, PlaneNormal);<br>
                        &nbsp; &nbsp;     vec3 d = Len * PlaneNormal;<br>
                        &nbsp; &nbsp;     return (Point - d);<br>
                        }
                        </code>
                        <p>
                                This function is used by CalcPositions() to project a midpoint on the plane defined by the
                                nearest vertex and its normal. The idea is that by doing a dot product between the normal and
                                the vector 'v' from the vertex to the point we want to project we get the length of the
                                projection of 'v' on the normal (the normal must be of unit length). This is exactly the distance
                                between the point and the closest point on the plane (i.e. its projection). We multiply the length
                                by the normal and substract it from point in order to reach the projection. The following picture
                                illustrates this calculation:
                        </p>
                        <img class="center" src="cp_projection2.jpg">
                        <p>
                                P<sub>1</sub> and P<sub>2</sub> are located on different half spaces created by the plane. When we
                                project v<sub>1</sub> on the green normal we get the length of d<sub>1</sub>. Multiply that length
                                by the normal to receive d<sub>1</sub> itself. Now substract it from P<sub>1</sub> to get its
                                projection on the plane. When we project v<sub>2</sub> on the green normal we get the length of d<sub>2</sub>
                                but it is a negative value. Multiply that by the normal to receive d<sub>2</sub> itself (negative length
                                means it reverses the normal). Now substract it from P<sub>2</sub> to get its projection on the plane. The
                                conclusion: this method works correctly no matter on which side of the plane our point is.
                        </p>
                        <p>(lighting.es)</p>
                        <code>
                        #version 410 core<br>
                        <br>
                        layout(triangles, equal_spacing, ccw) in;<br>
                        <br>
                        uniform mat4 gVP;<br>
                        <br>
                        <b>struct OutputPatch<br>
                        {<br>
                        &nbsp; &nbsp;    vec3 WorldPos_B030;<br>
                        &nbsp; &nbsp;     vec3 WorldPos_B021;<br>
                        &nbsp; &nbsp;     vec3 WorldPos_B012;<br>
                        &nbsp; &nbsp;     vec3 WorldPos_B003;<br>
                        &nbsp; &nbsp;     vec3 WorldPos_B102;<br>
                        &nbsp; &nbsp;     vec3 WorldPos_B201;<br>
                        &nbsp; &nbsp;     vec3 WorldPos_B300;<br>
                        &nbsp; &nbsp;     vec3 WorldPos_B210;<br>
                        &nbsp; &nbsp;     vec3 WorldPos_B120;<br>
                        &nbsp; &nbsp;     vec3 WorldPos_B111;<br>
                        &nbsp; &nbsp;     vec3 Normal[3];<br>
                        &nbsp; &nbsp;     vec2 TexCoord[3];<br>
                        };<br>
                        <br>
                        in patch OutputPatch oPatch;</b><br>
                        <br>
                        out vec3 WorldPos_FS_in;<br>
                        out vec2 TexCoord_FS_in;<br>
                        out vec3 Normal_FS_in;<br>
                        <br>
                        vec2 interpolate2D(vec2 v0, vec2 v1, vec2 v2)<br>
                        {<br>
                        &nbsp; &nbsp;     return vec2(gl_TessCoord.x) * v0 + vec2(gl_TessCoord.y) * v1 + vec2(gl_TessCoord.z) * v2;<br>
                        }<br>
                        <br>
                        vec3 interpolate3D(vec3 v0, vec3 v1, vec3 v2)<br>
                        {<br>
                        &nbsp; &nbsp;     return vec3(gl_TessCoord.x) * v0 + vec3(gl_TessCoord.y) * v1 + vec3(gl_TessCoord.z) * v2;<br>
                        }<br>
                        <br>
                        void main()<br>
                        {<br>
                        &nbsp; &nbsp;     // Interpolate the attributes of the output vertex using the barycentric coordinates<br>
                        &nbsp; &nbsp;     TexCoord_FS_in = interpolate2D(oPatch.TexCoord[0], oPatch.TexCoord[1], oPatch.TexCoord[2]);<br>
                        &nbsp; &nbsp;     Normal_FS_in = interpolate3D(oPatch.Normal[0], oPatch.Normal[1], oPatch.Normal[2]);<br>
                        <br>
                        <b>&nbsp; &nbsp;     float u = gl_TessCoord.x;<br>
                        &nbsp; &nbsp;     float v = gl_TessCoord.y;<br>
                        &nbsp; &nbsp;     float w = gl_TessCoord.z;<br>
                        <br>
                        &nbsp; &nbsp;     float uPow3 = pow(u, 3);<br>
                        &nbsp; &nbsp;     float vPow3 = pow(v, 3);<br>
                        &nbsp; &nbsp;     float wPow3 = pow(w, 3);<br>
                        &nbsp; &nbsp;     float uPow2 = pow(u, 2);<br>
                        &nbsp; &nbsp;     float vPow2 = pow(v, 2);<br>
                        &nbsp; &nbsp;     float wPow2 = pow(w, 2);<br>
                        <br>
                        &nbsp; &nbsp;     WorldPos_FS_in = oPatch.WorldPos_B300 * wPow3 +<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;                           oPatch.WorldPos_B030 * uPow3 +<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;                           oPatch.WorldPos_B003 * vPow3 +<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;           oPatch.WorldPos_B210 * 3.0 * wPow2 * u +<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;           oPatch.WorldPos_B120 * 3.0 * w * uPow2 +<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;           oPatch.WorldPos_B201 * 3.0 * wPow2 * v +<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;           oPatch.WorldPos_B021 * 3.0 * uPow2 * v +<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;           oPatch.WorldPos_B102 * 3.0 * w * vPow2 +<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;           oPatch.WorldPos_B012 * 3.0 * u * vPow2 +<br>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;           oPatch.WorldPos_B111 * 6.0 * w * u * v;</b><br>
                        <br>
                        &nbsp; &nbsp;     gl_Position = gVP * vec4(WorldPos_FS_in, 1.0);<br>
                        }
                        </code>
                        <p>
                                This is the entire TES with changes from the previous tutorial marked in bold face. The normal and
                                texture coordinates are interpolated the same as before. In order to calculate the world space position
                                we plug the barycentric coordinates into the Bezier triangle equation we saw in the background section.
                                The builtin function <i>pow()</i> is used in order to calculate the power of a number. We transform the
                                world space position to clip space and continue as usual.
                        </p>
                </section>

                <a href="../tutorial32/tutorial32.html" class="next highlight"> Next tutorial </a>
        </article>

        <script src="../html5shiv.min.js"></script>
        <script src="../html5shiv-printshiv.min.js"></script>

        <div id="disqus_thread"></div>
        <script type="text/javascript">
         /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
         var disqus_shortname = 'ogldevatspacecouk'; // required: replace example with your forum shortname
         var disqus_url = 'http://ogldev.atspace.co.uk/www/tutorial31/tutorial31.html';

         /* * * DON'T EDIT BELOW THIS LINE * * */
         (function() {
             var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
             dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
             (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
         })();
        </script>
        <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>

</body>
</html>
